<?php
/**
 * model基类
 *
 *
 */
class ModelsBase {
    private $_pdo;
    private static $_instance = null;
    private $_where = array();
    private $_select = '*';
    private $_log = '';

    protected $db = 'master';
    protected $table;

    private function __construct() {
        $this->_log = ROOT_PATH.DIRECTORY_SEPARATOR.'logs'.DIRECTORY_SEPARATOR.'error_sql.log';   

        $this->_pdo = $this->_connect();
        $this->init();
    }

    /**
     * 连接数据库
     *
     * @return PDO
     */
    private function _connect() {
 
        $dbConfig = LibsCommon::getConfig('databases');
        $config = $dbConfig[$this->db];//加载配置文件

        $config['port'] = empty($config['port']) ? 3306 : $config['port'];

        $dsn = "mysql:host={$config['host']};dbname={$config['dbName']};port={$config['port']}";
        $this->_pdo = LibsPdo::getInstance($dsn, $config['user'], $config['password'])->getPdo();

        DEBUG && $this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        $charset = LibsCommon::getConfig('charset');
        $this->_pdo->query("set names {$charset}");

        return $this->_pdo;
    }

    /**
     * 切换数据库
     *
     * @return PDO
     */
    public function changeDb($db = 'slave') {
        $this->db = $db;

        unset($this->_pdo);
        $this->_pdo = $this->_connect();

        return $this;
    }

    /**
     * 初始化
     *
     */
    protected function init() {

    }
    
    /**
     * single模式
     *
     */
    public static function getInstance() {
        $cls = get_called_class();

        //我现在严重怀疑self::$_instance只是Modelsbase的
        if(empty(self::$_instance) || get_class(self::$_instance) != $cls){
            self::$_instance = new $cls();
        }
        return self::$_instance;
    }
    
    /**
     * 设定表名
     * @param string $tablename
     * @return class model_base
     */
    public function table($tablename){
        $this->_where = array();
        $this->_select = '*';
        $this->table = $tablename;
        return $this;
    }

    public function select($cols = '*') {
        $this->_select = $cols;
    }
    
    /**
     * @param string $sql 
     *
     * @return unkown_type
     */
    public function query($sql, $mode=PDO::FETCH_ASSOC) {
        $stm = $this->_pdo->query($sql);
        if ($stm === false || $stm->setFetchMode($mode) === false) {
            $this->_errorLog();
        } 

        return $stm;
    }

    /**
     * 查询条件
     *
     * @params string $col 字段 
     * @params string $value 值 
     * @params string $equal 符号=,<> like 
     *
     * @return this;
     */
    public function where($col, $value, $equal='=') {
        $this->_where[] = array($col, $value, $equal);                    
        return $this;
    }
    /**
     * 总数量
     *
     * @return int
     */
    public function count() {
        $sql = "select count(*) from {$this->table} where 1";
        $sql .= $this->buildWhere();
        
        $stm = $this->query($sql);
        if ($stm === false) {
            return false;
        } 
        
        return $stm->fetchColumn();
    }

    /**
     * 获取全部数据
     *
     * @param int $limt 条数
     * @param int $start 开始值 
     * @param string $order 排序id desc, name desc 
     *
     * @return array
     */
    public function all($limit=0, $start=0, $order='') {
        $limit = intval($limit);
        $start = intval($start);
        $sql = "select {$this->_select} from {$this->table} where 1";
        $sql .= $this->buildWhere();

        !empty($order) && ($sql .= " order by {$order}");
        if (!empty($limit)) {
            if (!empty($start)) {
                $sql .= " limit {$start}, {$limit}";
            } else {
                $sql .= "limit {$limit}";
            }
        }

        $stm = $this->query($sql);
        if ($stm === false) {
            return false;
        }
        
        return $stm->fetchAll();
    }

    /**
     * 返回sql
     *
     * @return string
     */
    protected function buildWhere() {
        $sql = '';
        foreach ($this->_where as $value) {
            $equal = empty($value[2])?'=':$value[2];
            if (is_array($value[1])) {
                $strTmp = '(';
                foreach ($value[1] as $v) {
                    $strTmp .= $this->_pdo->quote($v).',';
                }
                $strTmp = trim($strTmp, ',').')';
                $sql .= " and {$value[0]} {$equal} ".$strTmp;        
            } else {
                $sql .= " and {$value[0]} {$equal} ". $this->_pdo->quote($value[1]);
            }
        }
        return $sql;
    }
    
    /**
     * 单表查询sql组装
     *
     * @param array $params
     * array('cols'=>array('id','name'),
     *      'where'=>array(array('id',2),array('name','%wq%','like')),
     *      'order'=>'id asc,pid desc',
     *      'limit'=>'10,8'
     *      );
     * cols为要查询的字段，mode为fetch方式
     * mode = PDO::FETCH_ASSOC  PDO::FETCH_BOTH  ....具体参考pdo fetch_style属性 
     * 其实PDO::FETCH_ASSOC是常量数字，具体参考pdo
     * 
     * @return result set
     */
    private function _prepareSelect(array $params = array()) {
        $where = " where 1 ";
        
        //对输入不正确进行处理
        if(!empty($params)){
            $array_value = array('cols','where','order', 'limit');
            $tmpArr = array_diff(array_keys($params), $array_value);
            if (!empty($tmpArr)) {
                throw new Exception("fetchAll params are not right！");
            }
        }

        $colStr = empty($params['cols'])?'*':implode(',', $params['cols']);
        $sql = "select $colStr from $this->table ";

        $whereArr = empty($params['where'])?array():$params['where'];
        foreach($whereArr as $value){
            $equal = empty($value[2])?'=':$value[2];
            if (is_array($value[1])) {
                $strTmp = '(';
                foreach ($value[1] as $v) {
                    $strTmp .= $this->_pdo->quote($v).',';
                }
                $strTmp = trim($strTmp, ',').')';
                $where .= " and {$value[0]} {$equal} ".$strTmp;        
            } else {
                $where .= " and {$value[0]} {$equal} ".$this->_pdo->quote($value[1]); 
            }
        }
        
        $sql .= $where;

        !empty($params['order']) && ($sql .= " order by {$params['order']}");
        !empty($params['limit']) && ($sql .= " limit {$params['limit']}");

        $stm = $this->_pdo->prepare($sql);
        if ($stm === false || $stm->execute() === false) {
            $this->_errorLog($stm);
            return false;
        }

        return $stm;
    }

    /**
     * 获取所有数据
     *
     * @return array
     */
    public function fetchAll(array $params = array(), $mode = PDO::FETCH_ASSOC){
        $stm = $this->_prepareSelect($params);
        return $stm->fetchAll($mode);
    }

    /**
     * 获取一条数据
     *
     * @param array $params
     *
     * @return array
     */
    public function fetch(array $params = array(), $mode = PDO::FETCH_ASSOC){
        $stm = $this->_prepareSelect($params);
        return $stm->fetch($mode);
    }
    
    /**
     * 插入一条数据  insert into users(name,pass) values ('a','a')
     *
     * @param array $params数组形式 ,如array('id'=>1,'name'=>'wq');
     * @param bool  $isReplace replace|insert
     *
     * @return int|boolean 
     */
    public function insert(array $params, $isReplace=false){
        $keys = implode(',', array_keys($params));
        $prepareValues = implode(',', array_fill(1,count($params), '?'));

        $insertKey = $isReplace ? 'REPLACE' : 'INSERT';
        $sql = "{$insertKey} INTO {$this->table} ({$keys}) values ({$prepareValues})";

        $stm = $this->_pdo->prepare($sql);
        if ($stm === false || $stm->execute(array_values($params)) === false) {
            $this->_errorLog($stm);
            return false;
        }

        return $this->_pdo->lastInsertId();
    }
    
    /**
     * 删除
     *
     * @params array $params删除条件
     * where array(array('id',1),array('name','wq%','like'))
     *
     * @return int|boolean 
     */
    public function delete(array $params = array()){
        $where = ' where 1 ';
        foreach($params as $key => $value){
            $equal = empty($value[2])?'=':$value[2];
            $where .= " and {$value[0]} {$equal} ".$this->_pdo->quote($value[1]); 
        }
        $sql = "DELETE FROM {$this->table} {$where}";
        $num = $this->_pdo->exec($sql);
        if ($num === false) {
            $this->_errorLog();
        }
        return $num;
    }
    
    /**
     * 更新数据
     *
     * @params array $where array('id'=>1,'name'=>'wq')
     * @params array $params array('name'=>'wqq')
     *
     * @return int|boolean 
     */
    public function update(array $params, array $filters=array()){
        $sql = "update {$this->table} ";
        $set = " set ";
        foreach($params as $key => $val){
            $set .= "  $key = '{$val}' ,";
        }
        $set = trim($set,',');
        $sql .= $set;
        
        $where = 'where 1';
        foreach($filters as $value){
            $equal = empty($value[2])?'=':$value[2];
            $where .= " and {$value[0]} {$equal} ".$this->_pdo->quote($value[1]); 
        }
        
        $sql .= $where;
        $num = $this->_pdo->exec($sql);
        if ($num === false) {
            $this->_errorLog();
        }
        return $num;
    }
    
    //多表联合查询
    
    /**
     * 获取一条数据
     * @param string $sql 标准的sql语句
     * @param int $mode
     * @return array result
     */
    public function getRow($sql,$mode=PDO::FETCH_ASSOC){
        $stm = $this->_pdo->prepare($sql);
        if ($stm === false || $stm->execute() === false) {
            $this->_errorLog($stm);
            return false;
        }
        return $stm->fetch($mode);
    }

    public function getOne($sql,$colunm=0) {
        $stm = $this->_pdo->prepare($sql);
        if ($stm === false || $stm->execute() === false) {
            $this->_errorLog($stm);
            return false;
        }
        return $stm->fetchColumn($colunm);
    }
    
    /**
     * 获取全部数据
     * @param string $sql 标准的sql语句
     * @param int $mode
     * @return array resultset
     */
    public function getAll($sql,$mode=PDO::FETCH_ASSOC){
        $stm = $this->_pdo->prepare($sql);
        if ($stm === false || $stm->execute() === false) {
            $this->_errorLog($stm);
            return false;
        }
        return $stm->fetchAll($mode);
    }

    /**
     *事物开始
     */
    public function startTransaction() {
        return $this->_pdo->beginTransaction();
    }

    /**
     *事物提交
     */
    public function commit() {
        return $this->_pdo->commit();
    }

    /**
     *事物回滚
     */
    public function rollback() {
        return $this->_pdo->rollBack();
    }

    /**
     *返回最后插入ID
     *
     */
    public function get_last_id() {
        return $this->_pdo->lastInsertId();
        /*
        $sql = "select last_insert_id()";
        return $this->getOne($sql);
        */
    }

    /**
     * error记录
     *
     */
    private function _errorLog($stm = null) {
        $message = date('Y-m-d H:i:s')."\t";
        $errInfo = empty($stm)?$this->_pdo->errorInfo():$stm->errorInfo();
        $message .= implode("\t", $errInfo).PHP_EOL;
        error_log($message, 3, $this->_log);
    }
}
